package com.zhouyer.edu.context;

import com.zhouyer.edu.annotation.MyAutoWired;
import com.zhouyer.edu.annotation.MyService;
import com.zhouyer.edu.annotation.MyTransactional;
import com.zhouyer.edu.factory.ProxyFactory;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 自定义容器上下文：主要进行包扫描，根据注解，通过反射生成bean待用
 *
 * @author zhouyer
 */
public class MyApplicationContext {
    private String packageName;

    /**
     * 用于存储bean
     */
    private Map<String, Object> beanMap = new ConcurrentHashMap<>();

    public MyApplicationContext(String packageName) {
        this.packageName = packageName;
        initBeans();
    }

    private void initBeans() {
        Reflections reflections = new Reflections(packageName);
        // 获取所有类是添加类@MyService注解和@MyRepository注解的类
        Set<Class<?>> myServiceTypeClass = reflections.getTypesAnnotatedWith(MyService.class);
        createBean(myServiceTypeClass);
        populateBean();
    }

    /**
     * 创建bean
     *
     * @param typsClass
     */
    private void createBean(Set<Class<?>> typsClass) {
        // 遍历这些类
        typsClass.forEach(clazz -> {
            // 获取实例对象
            Object o = null;
            try {
                o = clazz.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            //检查@MyService注解是否有value值，如果有，就以value值作为可以存入beanMap中，如果没有，则使用该类名，首字母小写作为key存入beanMap中
            MyService myService = clazz.getAnnotation(MyService.class);
            if (StringUtils.isNoneBlank(myService.value())) {
                beanMap.put(myService.value(), o);
            } else {
                // 获取首字母小写的类名,以其为key将对象放入map中
                beanMap.put(getClassFirstWordLower(clazz), o);
            }
        });
    }

    /**
     * 填充bean的属性，即通过@MyAutoWired注解注入的属性
     */
    private void populateBean() {
        for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
            // 获取对象
            Object object = entry.getValue();
            // 获取对象的Class
            Class<?> clazz = object.getClass();
            // 获取该类的所有属性
            Field[] fields = clazz.getDeclaredFields();
            // 获取该类的所有方法
            Method[] methods = clazz.getMethods();
            // 遍历属性，判断属性上是否有@MyAutowired，有则注入
            for (Field field : fields) {
                // 判断属性上是否有@MyAutowired
                if (field.isAnnotationPresent(MyAutoWired.class)) {
                    // 分割该字段的全限定类名
                    String[] classNames = field.getType().getName().split("\\.");
                    // 获取首字母小写的类名
                    String className = classNames[classNames.length - 1];
                    String key;
                    // @MyAutoWired注解中的value有值，则根据value的值去beanMap中取值
                    if (StringUtils.isNoneBlank(field.getAnnotation(MyAutoWired.class).value())) {
                        key = field.getAnnotation(MyAutoWired.class).value();
                    } else {
                        // 获取该字段对应类型的类的类名首字母小写名称
                        key = getFirstWordLower(className);
                    }
                    for (Method method : methods) {
                        if (method.getName().equalsIgnoreCase("set" + className)) {
                            try {
                                // 调用set方法设置属性
                                method.invoke(object, beanMap.get(key));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }

            /*********创建代理对象****************/
            if (clazz.isAnnotationPresent(MyTransactional.class)) {
                object = createProxyBean(clazz, object);
                // 直接覆盖原来的对象，因为要使用代理对象
                beanMap.put(entry.getKey(), object);
            }
        }
    }

    /**
     * 创建代理对象，如果类上有@MyTransactional注解
     *
     * @param typeClass
     */
    private Object createProxyBean(Class<?> typeClass, Object object) {
        // 获取代理工厂
        ProxyFactory proxyFactory = (ProxyFactory) beanMap.get("proxyFactory");
        // 判断该类是否有接口，如果实现类接口，就通过jdk动态代理，否则使用CGLIB动态代理技术
        Class<?>[] interfaces = typeClass.getInterfaces();
        if (ArrayUtils.isNotEmpty(interfaces)) {
            // 通过实现接口，使用JDK动态代理技术
            object = proxyFactory.getJdkProxy(object);
        } else {
            object = proxyFactory.getCglibProxy(object);
        }
        return object;
    }

    private String getClassFirstWordLower(Class<?> clazz) {
        String[] classNames = clazz.getName().split("\\.");
        // 获取首字母小写的类名
        return getFirstWordLower(classNames[classNames.length - 1]);
    }

    private String getFirstWordLower(String str) {
        char[] chars = str.toCharArray();
        // 如果首字母是大写，则转换成小写字母
        if (chars[0] >= 65 && chars[0] <= 90) {
            chars[0] += 32;
        }
        return String.valueOf(chars);
    }


    /**
     * 对外暴露的方法，用于获取bean
     *
     * @param id bean的id
     * @return
     */
    public Object getBean(String id) {
        return beanMap.get(id);
    }
    
}
